package com.gitee.kamismile.stone.rpc.grpc;

import com.gitee.kamismile.stone.commmon.base.ResultVO;
import com.gitee.kamismile.stone.commmon.errorEnum.ErrorEnum;
import com.gitee.kamismile.stone.commmon.exception.BusinessException;
import com.gitee.kamismile.stone.commmon.util.JsonUtil;
import io.grpc.*;
import org.apache.commons.lang.exception.ExceptionUtils;
import  org.slf4j.LoggerFactory;
import org.slf4j.Logger;

public class GlobalGrpcExceptionHandler implements ServerInterceptor {
    protected final Logger logger = LoggerFactory.getLogger(GlobalGrpcExceptionHandler.class);

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
                                                                 Metadata requestHeaders, ServerCallHandler<ReqT, RespT> next) {
        ServerCall.Listener<ReqT> delegate = next.startCall(call, requestHeaders);
        return new ExceptionHandlingServerCallListener<>(delegate, call, requestHeaders);
    }

    private class ExceptionHandlingServerCallListener<ReqT, RespT>
            extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
        private ServerCall<ReqT, RespT> serverCall;
        private Metadata metadata;

        ExceptionHandlingServerCallListener(ServerCall.Listener<ReqT> listener, ServerCall<ReqT, RespT> serverCall,
                                            Metadata metadata) {
            super(listener);
            this.serverCall = serverCall;
            this.metadata = metadata;
        }

        @Override
        public void onHalfClose() {
            try {
                super.onHalfClose();
            } catch (RuntimeException ex) {
                handleException(ex, serverCall, metadata);
                throw ex;
            }
        }

        @Override
        public void onReady() {
            try {
                super.onReady();
            } catch (RuntimeException ex) {
                handleException(ex, serverCall, metadata);
                throw ex;
            }
        }

        private void handleException(RuntimeException exception, ServerCall<ReqT, RespT> serverCall, Metadata metadata) {
            ResultVO resultVO = new ResultVO();
            resultVO.setSuccess(false);
            resultVO.setCode(String.valueOf(ErrorEnum.SYSTEM.getCode()));
            if (exception instanceof BusinessException) {
                BusinessException businessException = (BusinessException) exception;
                resultVO.setCode(String.valueOf(businessException.getCode()));
                resultVO.setData(businessException.getData());
            }
            logger.error(ExceptionUtils.getFullStackTrace(exception));


            resultVO.setMessage(exception.getMessage());
            serverCall.close(Status.INTERNAL.withDescription(JsonUtil.toJson(resultVO)), metadata);
        }
    }
}